angular.module("ui.ssnau.dockableFlexBox", [])
    .directive("dockableFlexBox", function () {
        var dragging = false;
        var meta = {};
        var ndBox = null;
        var hasBindDoc = false;

        function setCss(elem, name, value) {
            var n = name.substr(0,1).toUpperCase() + name.substr(1),
                elem = elem.length ? elem[0] : elem;

            //`Moz` the M is capitalized.
            ['webkit', 'Moz', 'ms', 'o'].forEach(function(prefix){
                elem.style[prefix + n] = value;
            });
            elem.style[name] = value;
        }

        function bindEvent(elem) {
            var ndElem = $(elem);
            ndElem.on("mousedown", "[dockable-handler]", handleDragStart);
            ndElem.on("mousemove", "[dockable]", handleDragOver);
            ndElem.on("mouseup", "[dockable]", handleDrop);
            bindDocument();
        }

        function bindDocument() {
            if (hasBindDoc) return;
            $(document).on("mouseup", clear);
            hasBindDoc = true;
        }

        function handleDragStart(e) {
            clear();
            dragging = true;
            var dockable = $(this).closest('[dockable]');
            meta = {
                source: $(dockable)
            };
            // Make every element on page unselectable
            $('*').addClass('unselectable');

            console.log("start....")
        }

        function handleDragOver(e) {
            var ndThis = $(this);
            if (!meta.source) return;//如果完全没有meta.source，比如从外部拖文件，直接关掉
            if (ndThis[0] === meta.source[0]) return; //如果跟source是同一个元素，无视之

            e.stopPropagation();
            e.preventDefault();
            console.log("over....")

            var region = computeRegion(ndThis, e);
            if (!region) {
                removeBox();
                return;
            }

            var direction = (region === "up" || region === "down") ? "column" : "row";
            drawBox(ndThis, region);
            meta.direction = direction;
            meta.region = region;

        }

        function clear() {
            //clear shits.
            dragging = false;
            removeBox();
            meta = {};
            $('*').removeClass('unselectable');
        }

        function handleDrop(e) {
            var ndTarget = $(this),
                ndSource = $(meta.source);

            if (!ndSource.length) {
                clear();
                return;
            }

            if (ndSource[0] == ndTarget[0]) {
                clear();
                return;
            }
            console.log("drop....")

            e.stopPropagation();
            e.preventDefault();

            //当两个对象具有相同父节点时，最简单情况。
            if (ndTarget.parent()[0] == ndSource.parent()[0]) {
                var ndParent = ndTarget.parent(),
                    sourceFisrt = (meta.region === "left" || meta.region === "up"),
                    order1 = 10,
                    order2 = 15,
                    order3 = 20;

                setCss(ndParent, "flexDirection", meta.direction);
                setCss(ndSource, "order", sourceFisrt ? order1 : order3);

                //We assume dockable flex box always allow user resize the inner box
                var ndDelimeter = ndParent.find(">.delimeter");
                if (!ndDelimeter.length) {
                    $("<div class='delimeter'></div>").appendTo(ndParent);
                }
                setCss(ndDelimeter, "order", order2);
                ndDelimeter.removeClass("delimeter-column");
                ndDelimeter.removeClass("delimeter-row");
                ndDelimeter.addClass("delimeter-" + meta.direction);
                setCss(ndTarget, "order", sourceFisrt ? order3 : order1);

                initDirectionProperties(meta.direction, [ndSource, ndTarget]);
            }

            //TODO: other condition.
            clear();

            //执行fb-onrsize中对应的代码
            ndTarget.data("fbOnresize") && ndTarget.data("fbOnresize")();
            ndSource.data("fbOnresize") && ndSource.data("fbOnresize")();

            function initDirectionProperties(direction, nodes) {
                angular.forEach(nodes, function (node) {
                    node = $(node);
                    node.css("width", "auto");
                    node.css("height", "auto");
                    setCss(node, 'flex', 1);
                });
            }
        }

        function getDockableId(node) {
            if (!node.data("dockable-id")) {
                node.data("dockable-id", s4() + s4());
            }
            return node.data("dockable-id");
        }

        function removeBox() {
            ndBox && ndBox.remove();
            ndBox = null;
        }

        function drawBox(node, region) {
            var direction = (region === "up" || region === "down") ? "column" : "row";
            var config = getDockableId(node) + region;

            if (meta.lastBoxConfig == config && ndBox) {
                return;
            };

            removeBox();

            var demension = {width: node.width(), height: node.height()},
                isFirst = region === 'up' || region === 'left';

            var html = ("<div class='flex' style='flex-direction:{{direction}};-webkit-flex-direction:{{direction}};position:absolute;left:0;top:0;z-index:9999;width:{{width}}px;height:{{height}}px'>" +
                "<div class='f1' style='border:2px #528CE0 solid;margin:2px;background:{{box1-bg}}'></div>" +
                "<div class='f1' style='border:2px #528CE0 solid;margin:2px;background:{{box2-bg}}'></div>" +
                "</div>").replace("{{direction}}", direction).replace("{{direction}}", direction)
                .replace("{{width}}", demension.width)
                .replace("{{height}}", demension.height)
                .replace("{{box1-bg}}", function(){
                    return isFirst ? "rgba(200,200,200,.4)" : "none";
                })
                .replace("{{box2-bg}}", function(){
                    return !isFirst ? "rgba(200,200,200,.4)" : "none";
                });

            ndBox = $(html);

            meta.lastBoxConfig = config;
            ndBox.appendTo($(node));
        }

        function computeRegion(node, e) {
            var offset = node.offset(), /*{top: int, left: int}*/
                demension = {width: node.width(), height: node.height()}, /*{width: int, height: int}*/
                pos = {left: e.pageX || e.originalEvent.pageX, top: e.pageY || e.originalEvent.pageY};

            //计算出当前位置离四条边的距离
            var info = {
                    up: pos.top - offset.top,
                    down: offset.top + demension.height - pos.top,
                    left: pos.left - offset.left,
                    right: offset.left + demension.width - pos.left
                },
                result = "up";

            angular.forEach(info, function (v, k) {
                result = (v < info[result]) ? k : result;
            });

            return result;
        }

        function s4() {
            return Math.floor((1 + Math.random()) * 0x10000)
                .toString(16)
                .substring(1);
        };

        function activeChild(elem) {
            var ndDockables = $(elem).find("[dockable]")
            angular.forEach(ndDockables, function(dockable) {
                if ($(dockable).css("position") === "static") {
                    $(dockable).css("position", "relative");
                }
            });
        }

        function _closeDockable(dockable) {
            $(dockable).hide();

            var ndContainer = $(dockable).parent();
            ndContainer.find('>.delimeter').hide();
            angular.forEach(ndContainer.find('[dockable]'),function(dockable){
                dockable.style.webkitFlex = 1;
                dockable.style.flex = 1;
                $(dockable).data("fbOnresize") && $(dockable).data("fbOnresize")();
            });
        }

        function _showDockable(dockable) {
            $(dockable).show();
            var ndContainer = $(dockable).parent();
            ndContainer.find('>.delimeter').hide();
            var count = 0;
            angular.forEach(ndContainer.find('[dockable]'),function(dockable){
                if (dockable.style.display !== "none") count++;
                dockable.style.webkitFlex = 1;
                dockable.style.flex = 1;
                $(dockable).data("fbOnresize") && $(dockable).data("fbOnresize")();
            });

            if (count == 2) {
                ndContainer.find('>.delimeter').show();
            }
        }

        function bindClose(elem) {
            elem.on("click","[dfb-hide-dockable]", function(){
                _closeDockable($(this).parents("[dockable]")[0]);
            });
        }

        return {
            restrict: "A",
            link: function (scope, elem, attr) {
                activeChild(elem);
                bindEvent(elem);
                bindClose(elem);

                angular.extend(scope, {
                    toggleDockable: function (arg, bShow) {
                        var dockable = typeof arg === "string" ? document.getElementById(arg) : $(arg)[0];
                        if (!dockable.hasAttribute("dockable")) {
                            console.error("The target dockable {{id}} seems not to be a dockable".replace("{{id}}", id));
                        }

                        if (bShow) {
                            _showDockable(dockable);
                        } else {
                            _closeDockable(dockable);
                        }

                    }
                });

            }
        };
    })
/**
 * 1. 监听ngShow的变化，一旦改变，调用相应的show/close方法，引发容器变化
 */
    .directive("dockable", function(){
        return {
            link: function(scope, elem, attr) {
                if (attr.ngShow) {
                    scope.$watch(attr.ngShow, function(show) {
                           scope.toggleDockable(elem, show);
                    });
                }
            }
        }
    })





/**
 用法：
 <div dockable-flex-box> <!-- 定义了一个dockable flex box-->
    <div dockable>     <!-- 定义了其中的一个dockable的部件 -->
        <div dockable-handler></div>   <!-- handler是给用户拖拽的 -->
        <button dfb-hide-dockable>close</button> <!-- 点击这个按钮关闭 -->
    </div>
    <div dockable><div>
 </div>







 **/